import time 
import logging
import numpy as np
import gradio as gr
import cv2
import faiss
import joblib
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier, VotingClassifier, BaggingClassifier, AdaBoostClassifier
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from xgboost import XGBClassifier
from sklearn.ensemble import StackingClassifier
from tabulate import tabulate
from util import createXY 

#配置logging，打印进度条和函数进行的状态
logging.basicConfig(level=logging.INFO,format='%(asctime)s-%(levelname)s-%(message)s')

# 读取数据
X, y = createXY(train_folder="../data/train", dest_folder=".",method='flat')
X=np.array(X).astype('float32')
faiss.normalize_L2(X)
y=np.array(y)
logging.info('数据加载和与处理完成')

#划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=2023)
logging.info('数据划分为训练集和测试集')

#创建逻辑回归分类器
logistic_clf=LogisticRegression(max_iter=1000)

#创建随机森林分类器
random_forest_clf=RandomForestClassifier(random_state=42)

# 创建支持向量机分类器
# probability=True表示需要计算概率值，这样才能使用软投票
svm_clf = SVC(probability=True)

# 创建硬投票分类器，包含逻辑回归、随机森林和支持向量机三个分类器
voting_clf_hard = VotingClassifier(
    estimators=[('lr', logistic_clf), ('rf', random_forest_clf), ('svc', svm_clf)],
    voting='hard' # 采用硬投票, 即选择票数最多的类别作为预测结果
)

# 创建软投票分类器，包含逻辑回归、随机森林和支持向量机三个分类器
voting_clf_soft = VotingClassifier(
    estimators=[('lr', logistic_clf), ('rf', random_forest_clf), ('svc', svm_clf)],
    voting='soft' # 采用软投票, 即选择概率之和最大的类别作为预测结果
)

# 创建Bagging分类器
bag_clf = BaggingClassifier(
    DecisionTreeClassifier(), # 基分类器选用决策树分类器
    n_estimators=500,         # 500个分类器
    max_samples=100,          # 每个分类器的训练集包含100个样本
    bootstrap=True,           # 有放回的采样
    n_jobs=-1,                # 使用所有CPU核
)

# 创建Pasting分类器
paste_clf = BaggingClassifier(
    DecisionTreeClassifier(), # 基分类器选用决策树分类器
    n_estimators=500,         # 500个分类器
    max_samples=100,          # 每个分类器的训练集包含100个样本
    bootstrap=False,          # 无放回的采样
    n_jobs=-1,                # 使用所有CPU核
)

# 创建AdaBoost分类器
ada_clf = AdaBoostClassifier(
    DecisionTreeClassifier(max_depth=1),  # 基分类器选用决策树分类器
    n_estimators=200,                     # 200个分类器
    algorithm="SAMME.R",                  # 使用SAMME.R算法
    learning_rate=0.5                     # 学习率为0.5, 即每个分类器的权重缩减系数为0.5
) 

# 创建一个梯度提升分类器
xgb_clf = XGBClassifier(
    n_estimators=200,   # 200个分类器
    max_depth=2,        # 每个分类器的最大深度为2
    learning_rate=0.5   # 学习率为0.5, 即每个分类器的权重缩减系数为0.5
)

# 创建一个堆叠分类器
stacking_clf = StackingClassifier(
    estimators=[('lr', logistic_clf), ('rf', random_forest_clf), ('svc', svm_clf)],
    final_estimator=LogisticRegression() # 最终分类器选用逻辑回归分类器
)

clfs = {
    "logistic_regression": logistic_clf,
    "random_forest": random_forest_clf,
    "svm": svm_clf,
    "hard_voting": voting_clf_hard,
    "soft_voting": voting_clf_soft,
    "bagging": bag_clf,
    "pasting": paste_clf,
    "adaboost": ada_clf,
    "gradient_boosting": xgb_clf,
    "stacking": stacking_clf
}

results = []

# 训练和评估分类器
for name, clf in clfs.items():# 遍历字典中的每一个键值对:(模型名称, 模型对象)
    start_time = time.time()
    clf.fit(X_train, y_train)
    fit_time = time.time() - start_time
    logging.info(f"{name}模型训练完成, 用时{fit_time:.4f}s")

    start_time = time.time()
    accuracy = clf.score(X_test, y_test)
    score_time = time.time() - start_time
    logging.info(f"{name}模型评估完成, 用时{score_time:.4f}s")

    results.append([name, fit_time, score_time, accuracy])

# 打印结果表格
headers = ["Classifier", "Training Time (s)", "Prediction Time (s)", "Accuracy"]
print(tabulate(results, headers=headers, tablefmt="simple"))

#初始化变量，跟踪最佳的k值和相应的准确率
best_accuracy = 0
best_model = None
best_model_name = None

#训练和评估分类器
for name, cif in clfs.items():
    if accuracy > best_accuracy:
        best_accuracy = accuracy
        best_model = clf
        best_model_name = name

#保存准确率最高的模型
joblib.dump(best_model, 'best_model.pkl')
print(f"准确率最高的模型是: {best_model_name}，准确率为{best_accuracy:.4f}。")

#创建gradio界面

# 加载最佳模型
best_model = joblib.load('best_model.pkl')

def classify_image(image):
    image = cv2.imdecode(np.fromstring(image.read(),np.uint8),cv2.IMREAD_COLOR)
    image = image.resize((224, 224))  # 将图像大小调整为 224x224
    image = np.array(image) / 255.0  # 将图像转为numpy数组，并将像素值范围改为 [0, 1]
    faiss.normalize_L2(image)

    prediction = best_model.predict(image)  # 使用模型进行预测

    return "猫" if prediction[0]==0 else "狗"  # 返回字典

# 创建 Gradio 界面
iface = gr.Interface(fn=classify_image, inputs="image", outputs="text")

# 启动 Gradio 界面
iface.launch(share=True)